﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration.Provider;
using DbDoc.BusinessObjects;
using System.Configuration;
using System.Web.Configuration;

namespace DbDoc.Provider.Persistence
{

    public abstract class PersistenceProvider : ProviderBase
    {
        public abstract string ConnectionInfo { get; }
        public abstract string StorageName { get; }

        public abstract string Name { get; }

        //Methods
        public abstract IList<Database> GetDatabases();
        public abstract IList<Table> GetTables(Database db);
        public abstract IList<View> GetViews(Database db);
        public abstract IList<StoredProc> GetStoredProcs(Database db);
        public abstract IList<Column> GetColumns(Database db, Table tbl);
        public abstract IEnumerable<Comment> GetCommentsHistory(DbObject obj);

        public abstract void SaveDbObject(DbObject obj);
        public abstract void UpdateDbObject(DbObject obj);

        public abstract bool ValidateUser(string username, string password);
        public abstract bool ValidateUser(AppUser user);
        public abstract AppUser Login(string username, string password);
        public abstract AppUser GetUserByUsername(string username);
        public abstract IList<AppUser> GetAllUsers();

        public abstract Setting GetByKey(string key);
        public abstract IList<Setting> GetAll();


        /// <summary>
        /// Checks if the system requires the user to login. If it is required, the user will be shown a login box.
        /// </summary>
        /// <returns>True if the user is required to login.</returns>
        public bool RequireLogin()
        {
            bool requireLogin = false;
            Setting setting = GetByKey(Constants.Settings.REQUIRE_LOGIN);
            if (setting != null && (bool)setting.Value == true)
                requireLogin = true;
            return requireLogin;
        }

        /// <summary>
        /// Updates the Login Required setting.
        /// </summary>
        /// <param name="required">true, if the user is required to login to use the software, else false</param>
        public void UserLoginRequired(bool required)
        {
            Setting newValue = new Setting() { Key = Constants.Settings.REQUIRE_LOGIN, DataType = "bool", RawValue = required.ToString() };
            SaveSetting(newValue);
        }

        /// <summary>
        /// Adds the settings to the db if it is a new one otherwise updates the existing setting
        /// </summary>
        /// <param name="input"></param>
        public abstract void SaveSetting(Setting input);

        /// <summary>
        /// Checks if a connection can be established with the underlying Data Source
        /// </summary>
        /// <returns>True - if the connection was made, else false</returns>
        public abstract bool TestConnection();

        public abstract void SaveUser(AppUser user);
    }

    public class PersistenceProviderCollection : ProviderCollection
    {
        public override void Add(ProviderBase provider)
        {
            if (provider == null)
                throw new ArgumentNullException("The provider parameter cannot be null.");

            if (!(provider is PersistenceProvider))
                throw new ArgumentException("The provider parameter must be of type Persistence.");

            base.Add(provider);
        }

        new public PersistenceProvider this[string name]
        {
            get { return (PersistenceProvider)base[name]; }
        }

        public void CopyTo(PersistenceProvider[] array, int index)
        {
            base.CopyTo(array, index);
        }
    }

    public class PersistenceProviderConfiguration : ConfigurationSection
    {
        [ConfigurationProperty("providers")]
        public ProviderSettingsCollection Providers
        {
            get
            {
                return (ProviderSettingsCollection)base["providers"];
            }
        }

        [ConfigurationProperty("defaultProvider", DefaultValue = "SqlProvider")]
        [StringValidator(MinLength = 1)]
        public string DefaultProvider
        {
            get
            {
                return (string)base["defaultProvider"];
            }
            set
            {
                base["defaultProvider"] = value;
            }
        }
    }

    public class PersistenceProviderManager
    {
        //Initialization related variables and logic
        private static bool isInitialized = false;
        private static Exception initializationException;

        private static object initializationLock = new object();

        static PersistenceProviderManager()
        {
            Initialize();
        }

        private static void Initialize()
        {
            try
            {
                //Get the feature's configuration info
                PersistenceProviderConfiguration qc =
                    (PersistenceProviderConfiguration)ConfigurationManager.GetSection("PersistenceProvider");

                if (qc.DefaultProvider == null || qc.Providers == null || qc.Providers.Count < 1)
                    throw new ProviderException("You must specify a valid default provider.");

                //Instantiate the providers
                providerCollection = new PersistenceProviderCollection();
                ProvidersHelper.InstantiateProviders(qc.Providers, providerCollection, typeof(PersistenceProvider));
                providerCollection.SetReadOnly();
                defaultProvider = providerCollection[qc.DefaultProvider];
                if (defaultProvider == null)
                {
                    throw new ConfigurationErrorsException(
                        "You must specify a default provider for the feature.",
                        qc.ElementInformation.Properties["defaultProvider"].Source,
                        qc.ElementInformation.Properties["defaultProvider"].LineNumber);
                }
            }
            catch (Exception ex)
            {
                initializationException = ex;
                isInitialized = false;
                throw ex;
            }

            isInitialized = true; //error-free initialization
        }

        public static void SetDefaultProvider(PersistenceProvider provider)
        {
            //Get the feature's configuration info
            PersistenceProviderConfiguration qc =
                (PersistenceProviderConfiguration)ConfigurationManager.GetSection("PersistenceProvider");

            if (qc.DefaultProvider == null || qc.Providers == null || qc.Providers.Count < 1)
                throw new ProviderException("You must specify a valid default provider.");
            foreach (PersistenceProvider p in qc.Providers)
                if (p.Name == provider.Name)
                {
                    qc.DefaultProvider = p.Name;
                    //TODO: Save the configuration here.
                    break;
                }


        }

        //Public feature API
        private static PersistenceProvider defaultProvider;
        private static PersistenceProviderCollection providerCollection;

        public static PersistenceProvider Provider
        {
            get
            {
                return defaultProvider;
            }
        }

        public static PersistenceProviderCollection Providers
        {
            get
            {
                return providerCollection;
            }
        }
    }
}
